// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/base/mime_util_internal.h"

#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "media/base/media.h"
#include "media/base/media_client.h"
#include "media/base/media_switches.h"
#include "media/base/video_codecs.h"

#if defined(OS_ANDROID)
#include "base/android/build_info.h"
#include "media/base/android/media_codec_util.h"
#endif

namespace media {
namespace internal {

    struct CodecIDMappings {
        const char* const codec_id;
        MimeUtil::Codec codec;
    };

    // List of codec IDs that provide enough information to determine the
    // codec and profile being requested.
    //
    // The "mp4a" strings come from RFC 6381.
    static const CodecIDMappings kUnambiguousCodecStringMap[] = {
        { "1", MimeUtil::PCM }, // We only allow this for WAV so it isn't ambiguous.
        // avc1/avc3.XXXXXX may be unambiguous; handled by ParseAVCCodecId().
        // hev1/hvc1.XXXXXX may be unambiguous; handled by ParseHEVCCodecID().
        // vp9, vp9.0, vp09.xx.xx.xx.xx.xx.xx.xx may be unambiguous; handled by
        // ParseVp9CodecID().
        { "mp3", MimeUtil::MP3 },
        // Following is the list of RFC 6381 compliant audio codec strings:
        //   mp4a.66     - MPEG-2 AAC MAIN
        //   mp4a.67     - MPEG-2 AAC LC
        //   mp4a.68     - MPEG-2 AAC SSR
        //   mp4a.69     - MPEG-2 extension to MPEG-1 (MP3)
        //   mp4a.6B     - MPEG-1 audio (MP3)
        //   mp4a.40.2   - MPEG-4 AAC LC
        //   mp4a.40.02  - MPEG-4 AAC LC (leading 0 in aud-oti for compatibility)
        //   mp4a.40.5   - MPEG-4 HE-AAC v1 (AAC LC + SBR)
        //   mp4a.40.05  - MPEG-4 HE-AAC v1 (AAC LC + SBR) (leading 0 in aud-oti for
        //                 compatibility)
        //   mp4a.40.29  - MPEG-4 HE-AAC v2 (AAC LC + SBR + PS)
        { "mp4a.66", MimeUtil::MPEG2_AAC },
        { "mp4a.67", MimeUtil::MPEG2_AAC },
        { "mp4a.68", MimeUtil::MPEG2_AAC },
        { "mp4a.69", MimeUtil::MP3 },
        { "mp4a.6B", MimeUtil::MP3 },
        { "mp4a.40.2", MimeUtil::MPEG4_AAC },
        { "mp4a.40.02", MimeUtil::MPEG4_AAC },
        { "mp4a.40.5", MimeUtil::MPEG4_AAC },
        { "mp4a.40.05", MimeUtil::MPEG4_AAC },
        { "mp4a.40.29", MimeUtil::MPEG4_AAC },
#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
        // TODO(servolk): Strictly speaking only mp4a.A5 and mp4a.A6 codec ids are
        // valid according to RFC 6381 section 3.3, 3.4. Lower-case oti (mp4a.a5 and
        // mp4a.a6) should be rejected. But we used to allow those in older versions
        // of Chromecast firmware and some apps (notably MPL) depend on those codec
        // types being supported, so they should be allowed for now
        // (crbug.com/564960).
        { "ac-3", MimeUtil::AC3 },
        { "mp4a.a5", MimeUtil::AC3 },
        { "mp4a.A5", MimeUtil::AC3 },
        { "ec-3", MimeUtil::EAC3 },
        { "mp4a.a6", MimeUtil::EAC3 },
        { "mp4a.A6", MimeUtil::EAC3 },
#endif
        { "vorbis", MimeUtil::VORBIS },
        { "opus", MimeUtil::OPUS },
        { "vp8", MimeUtil::VP8 },
        { "vp8.0", MimeUtil::VP8 },
        { "theora", MimeUtil::THEORA }
    };

    // List of codec IDs that are ambiguous and don't provide
    // enough information to determine the codec and profile.
    // The codec in these entries indicate the codec and profile
    // we assume the user is trying to indicate.
    static const CodecIDMappings kAmbiguousCodecStringMap[] = {
        { "mp4a.40", MimeUtil::MPEG4_AAC },
        { "avc1", MimeUtil::H264 },
        { "avc3", MimeUtil::H264 },
        // avc1/avc3.XXXXXX may be ambiguous; handled by ParseAVCCodecId().
    };

#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
    static const char kHexString[] = "0123456789ABCDEF";
    static char IntToHex(int i)
    {
        DCHECK_GE(i, 0) << i << " not a hex value";
        DCHECK_LE(i, 15) << i << " not a hex value";
        return kHexString[i];
    }

    static std::string TranslateLegacyAvc1CodecIds(const std::string& codec_id)
    {
        // Special handling for old, pre-RFC 6381 format avc1 strings, which are still
        // being used by some HLS apps to preserve backward compatibility with older
        // iOS devices. The old format was avc1.<profile>.<level>
        // Where <profile> is H.264 profile_idc encoded as a decimal number, i.e.
        // 66 is baseline profile (0x42)
        // 77 is main profile (0x4d)
        // 100 is high profile (0x64)
        // And <level> is H.264 level multiplied by 10, also encoded as decimal number
        // E.g. <level> 31 corresponds to H.264 level 3.1
        // See, for example, http://qtdevseed.apple.com/qadrift/testcases/tc-0133.php
        uint32_t level_start = 0;
        std::string result;
        if (base::StartsWith(codec_id, "avc1.66.", base::CompareCase::SENSITIVE)) {
            level_start = 8;
            result = "avc1.4200";
        } else if (base::StartsWith(codec_id, "avc1.77.",
                       base::CompareCase::SENSITIVE)) {
            level_start = 8;
            result = "avc1.4D00";
        } else if (base::StartsWith(codec_id, "avc1.100.",
                       base::CompareCase::SENSITIVE)) {
            level_start = 9;
            result = "avc1.6400";
        }

        uint32_t level = 0;
        if (level_start > 0 && base::StringToUint(codec_id.substr(level_start), &level) && level < 256) {
            // This is a valid legacy avc1 codec id - return the codec id translated
            // into RFC 6381 format.
            result.push_back(IntToHex(level >> 4));
            result.push_back(IntToHex(level & 0xf));
            return result;
        }

        // This is not a valid legacy avc1 codec id - return the original codec id.
        return codec_id;
    }
#endif

    static bool ParseVp9CodecID(const std::string& mime_type_lower_case,
        const std::string& codec_id,
        VideoCodecProfile* out_profile,
        uint8_t* out_level)
    {
        if (mime_type_lower_case == "video/mp4") {
            if (base::CommandLine::ForCurrentProcess()->HasSwitch(
                    switches::kEnableVp9InMp4)) {
                return ParseNewStyleVp9CodecID(codec_id, out_profile, out_level);
            }
        } else if (mime_type_lower_case == "video/webm") {
            // Only legacy codec strings are supported in WebM.
            // TODO(kqyang): Should we support new codec string in WebM?
            return ParseLegacyVp9CodecID(codec_id, out_profile, out_level);
        }
        return false;
    }

    static bool IsValidH264Level(uint8_t level_idc)
    {
        // Valid levels taken from Table A-1 in ISO/IEC 14496-10.
        // Level_idc represents the standard level represented as decimal number
        // multiplied by ten, e.g. level_idc==32 corresponds to level==3.2
        return ((level_idc >= 10 && level_idc <= 13) || (level_idc >= 20 && level_idc <= 22) || (level_idc >= 30 && level_idc <= 32) || (level_idc >= 40 && level_idc <= 42) || (level_idc >= 50 && level_idc <= 51));
    }

    MimeUtil::MimeUtil()
        : allow_proprietary_codecs_(false)
    {
#if defined(OS_ANDROID)
        platform_info_.is_unified_media_pipeline_enabled = IsUnifiedMediaPipelineEnabled();
        // When the unified media pipeline is enabled, we need support for both GPU
        // video decoders and MediaCodec; indicated by HasPlatformDecoderSupport().
        // When the Android pipeline is used, we only need access to MediaCodec.
        platform_info_.has_platform_decoders = ArePlatformDecodersAvailable();
        platform_info_.has_platform_vp8_decoder = MediaCodecUtil::IsVp8DecoderAvailable();
        platform_info_.has_platform_vp9_decoder = MediaCodecUtil::IsVp9DecoderAvailable();
        platform_info_.supports_opus = PlatformHasOpusSupport();
#endif

        InitializeMimeTypeMaps();
    }

    MimeUtil::~MimeUtil() { }

    VideoCodec MimeUtilToVideoCodec(MimeUtil::Codec codec)
    {
        switch (codec) {
        case MimeUtil::H264:
            return kCodecH264;
        case MimeUtil::HEVC:
            return kCodecHEVC;
        case MimeUtil::VP8:
            return kCodecVP8;
        case MimeUtil::VP9:
            return kCodecVP9;
        case MimeUtil::THEORA:
            return kCodecTheora;
        default:
            break;
        }
        return kUnknownVideoCodec;
    }

    SupportsType MimeUtil::AreSupportedCodecs(
        const CodecSet& supported_codecs,
        const std::vector<std::string>& codecs,
        const std::string& mime_type_lower_case,
        bool is_encrypted) const
    {
        DCHECK(!supported_codecs.empty());
        DCHECK(!codecs.empty());

        SupportsType result = IsSupported;
        for (size_t i = 0; i < codecs.size(); ++i) {
            bool is_ambiguous = true;
            Codec codec = INVALID_CODEC;
            VideoCodecProfile video_profile = VIDEO_CODEC_PROFILE_UNKNOWN;
            uint8_t video_level = 0;
            if (!StringToCodec(mime_type_lower_case, codecs[i], &codec, &is_ambiguous,
                    &video_profile, &video_level, is_encrypted)) {
                return IsNotSupported;
            }

            VideoCodec video_codec = MimeUtilToVideoCodec(codec);

            if (GetMediaClient() && video_codec != kUnknownVideoCodec && !GetMediaClient()->IsSupportedVideoConfig(video_codec, video_profile, video_level)) {
                return IsNotSupported;
            }

            if (!IsCodecSupported(codec, mime_type_lower_case, is_encrypted) || supported_codecs.find(codec) == supported_codecs.end()) {
                return IsNotSupported;
            }

            if (is_ambiguous)
                result = MayBeSupported;
        }

        return result;
    }

    void MimeUtil::InitializeMimeTypeMaps()
    {
#if defined(USE_PROPRIETARY_CODECS)
        allow_proprietary_codecs_ = true;
#endif

        for (size_t i = 0; i < arraysize(kUnambiguousCodecStringMap); ++i) {
            string_to_codec_map_[kUnambiguousCodecStringMap[i].codec_id] = CodecEntry(kUnambiguousCodecStringMap[i].codec, false);
        }

        for (size_t i = 0; i < arraysize(kAmbiguousCodecStringMap); ++i) {
            string_to_codec_map_[kAmbiguousCodecStringMap[i].codec_id] = CodecEntry(kAmbiguousCodecStringMap[i].codec, true);
        }

        AddSupportedMediaFormats();
    }

    // Each call to AddContainerWithCodecs() contains a media type
    // (https://en.wikipedia.org/wiki/Media_type) and corresponding media codec(s)
    // supported by these types/containers.
    // TODO(ddorwin): Replace insert() calls with initializer_list when allowed.
    void MimeUtil::AddSupportedMediaFormats()
    {
        CodecSet implicit_codec;
        CodecSet wav_codecs;
        wav_codecs.insert(PCM);

        CodecSet ogg_audio_codecs;
        ogg_audio_codecs.insert(OPUS);
        ogg_audio_codecs.insert(VORBIS);
        CodecSet ogg_video_codecs;
#if !defined(OS_ANDROID)
        ogg_video_codecs.insert(THEORA);
#endif // !defined(OS_ANDROID)
        CodecSet ogg_codecs(ogg_audio_codecs);
        ogg_codecs.insert(ogg_video_codecs.begin(), ogg_video_codecs.end());

        CodecSet webm_audio_codecs;
        webm_audio_codecs.insert(OPUS);
        webm_audio_codecs.insert(VORBIS);
        CodecSet webm_video_codecs;
        webm_video_codecs.insert(VP8);
        webm_video_codecs.insert(VP9);
        CodecSet webm_codecs(webm_audio_codecs);
        webm_codecs.insert(webm_video_codecs.begin(), webm_video_codecs.end());

#if defined(USE_PROPRIETARY_CODECS)
        CodecSet mp3_codecs;
        mp3_codecs.insert(MP3);

        CodecSet aac;
        aac.insert(MPEG2_AAC);
        aac.insert(MPEG4_AAC);

        CodecSet avc_and_aac(aac);
        avc_and_aac.insert(H264);

        CodecSet mp4_audio_codecs(aac);
        mp4_audio_codecs.insert(MP3);
#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
        mp4_audio_codecs.insert(AC3);
        mp4_audio_codecs.insert(EAC3);
#endif // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)

        CodecSet mp4_video_codecs;
        mp4_video_codecs.insert(H264);
#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
        mp4_video_codecs.insert(HEVC);
#endif // BUILDFLAG(ENABLE_HEVC_DEMUXING)
        // Only VP9 with valid codec string vp09.xx.xx.xx.xx.xx.xx.xx is supported.
        // See ParseVp9CodecID for details.
        mp4_video_codecs.insert(VP9);
        CodecSet mp4_codecs(mp4_audio_codecs);
        mp4_codecs.insert(mp4_video_codecs.begin(), mp4_video_codecs.end());
#endif // defined(USE_PROPRIETARY_CODECS)

        AddContainerWithCodecs("audio/wav", wav_codecs, false);
        AddContainerWithCodecs("audio/x-wav", wav_codecs, false);
        AddContainerWithCodecs("audio/webm", webm_audio_codecs, false);
        DCHECK(!webm_video_codecs.empty());
        AddContainerWithCodecs("video/webm", webm_codecs, false);
        AddContainerWithCodecs("audio/ogg", ogg_audio_codecs, false);
        // video/ogg is only supported if an appropriate video codec is supported.
        // Note: This assumes such codecs cannot be later excluded.
        if (!ogg_video_codecs.empty())
            AddContainerWithCodecs("video/ogg", ogg_codecs, false);
        // TODO(ddorwin): Should the application type support Opus?
        AddContainerWithCodecs("application/ogg", ogg_codecs, false);
        AddContainerWithCodecs("audio/flac", implicit_codec, false);

#if defined(USE_PROPRIETARY_CODECS)
        AddContainerWithCodecs("audio/mpeg", mp3_codecs, true); // Allow "mp3".
        AddContainerWithCodecs("audio/mp3", implicit_codec, true);
        AddContainerWithCodecs("audio/x-mp3", implicit_codec, true);
        AddContainerWithCodecs("audio/aac", implicit_codec, true); // AAC / ADTS.
        AddContainerWithCodecs("audio/mp4", mp4_audio_codecs, true);
        DCHECK(!mp4_video_codecs.empty());
        AddContainerWithCodecs("video/mp4", mp4_codecs, true);
        // These strings are supported for backwards compatibility only and thus only
        // support the codecs needed for compatibility.
        AddContainerWithCodecs("audio/x-m4a", aac, true);
        AddContainerWithCodecs("video/x-m4v", avc_and_aac, true);

#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
        // TODO(ddorwin): Exactly which codecs should be supported?
        DCHECK(!mp4_video_codecs.empty());
        AddContainerWithCodecs("video/mp2t", mp4_codecs, true);
#endif // BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
#if defined(OS_ANDROID)
        // HTTP Live Streaming (HLS).
        // TODO(ddorwin): Is any MP3 codec string variant included in real queries?
        CodecSet hls_codecs(avc_and_aac);
        hls_codecs.insert(MP3);
        AddContainerWithCodecs("application/x-mpegurl", hls_codecs, true);
        AddContainerWithCodecs("application/vnd.apple.mpegurl", hls_codecs, true);
        AddContainerWithCodecs("audio/mpegurl", hls_codecs, true);
        // Not documented by Apple, but unfortunately used extensively by Apple and
        // others for both audio-only and audio+video playlists. See
        // https://crbug.com/675552 for details and examples.
        AddContainerWithCodecs("audio/x-mpegurl", hls_codecs, true);
#endif // defined(OS_ANDROID)
#endif // defined(USE_PROPRIETARY_CODECS)
    }

    void MimeUtil::AddContainerWithCodecs(const std::string& mime_type,
        const CodecSet& codecs,
        bool is_proprietary_mime_type)
    {
#if !defined(USE_PROPRIETARY_CODECS)
        DCHECK(!is_proprietary_mime_type);
#endif

        media_format_map_[mime_type] = codecs;

        if (is_proprietary_mime_type)
            proprietary_media_containers_.push_back(mime_type);
    }

    bool MimeUtil::IsSupportedMediaMimeType(const std::string& mime_type) const
    {
        return media_format_map_.find(base::ToLowerASCII(mime_type)) != media_format_map_.end();
    }

    void MimeUtil::ParseCodecString(const std::string& codecs,
        std::vector<std::string>* codecs_out,
        bool strip)
    {
        *codecs_out = base::SplitString(base::TrimString(codecs, "\"", base::TRIM_ALL), ",",
            base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);

        // Convert empty or all-whitespace input to 0 results.
        if (codecs_out->size() == 1 && (*codecs_out)[0].empty())
            codecs_out->clear();

        if (!strip)
            return;

        // Strip everything past the first '.'
        for (std::vector<std::string>::iterator it = codecs_out->begin();
             it != codecs_out->end(); ++it) {
            size_t found = it->find_first_of('.');
            if (found != std::string::npos)
                it->resize(found);
        }
    }

    SupportsType MimeUtil::IsSupportedMediaFormat(
        const std::string& mime_type,
        const std::vector<std::string>& codecs,
        bool is_encrypted) const
    {
        const std::string mime_type_lower_case = base::ToLowerASCII(mime_type);
        MediaFormatMappings::const_iterator it_media_format_map = media_format_map_.find(mime_type_lower_case);
        if (it_media_format_map == media_format_map_.end())
            return IsNotSupported;

        if (it_media_format_map->second.empty()) {
            // We get here if the mimetype does not expect a codecs parameter.
            return (codecs.empty() && IsDefaultCodecSupportedLowerCase(mime_type_lower_case, is_encrypted))
                ? IsSupported
                : IsNotSupported;
        }

        if (codecs.empty()) {
            // We get here if the mimetype expects to get a codecs parameter,
            // but didn't get one. If |mime_type_lower_case| does not have a default
            // codec the best we can do is say "maybe" because we don't have enough
            // information.
            Codec default_codec = INVALID_CODEC;
            if (!GetDefaultCodecLowerCase(mime_type_lower_case, &default_codec))
                return MayBeSupported;

            return IsCodecSupported(default_codec, mime_type_lower_case, is_encrypted)
                ? IsSupported
                : IsNotSupported;
        }

#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
        if (mime_type_lower_case == "video/mp2t") {
            std::vector<std::string> codecs_to_check;
            for (const auto& codec_id : codecs) {
                codecs_to_check.push_back(TranslateLegacyAvc1CodecIds(codec_id));
            }
            return AreSupportedCodecs(it_media_format_map->second, codecs_to_check,
                mime_type_lower_case, is_encrypted);
        }
#endif

        return AreSupportedCodecs(it_media_format_map->second, codecs,
            mime_type_lower_case, is_encrypted);
    }

    void MimeUtil::RemoveProprietaryMediaTypesAndCodecs()
    {
        for (const auto& container : proprietary_media_containers_)
            media_format_map_.erase(container);
        allow_proprietary_codecs_ = false;
    }

    // static
    bool MimeUtil::IsCodecSupportedOnPlatform(
        Codec codec,
        const std::string& mime_type_lower_case,
        bool is_encrypted,
        const PlatformInfo& platform_info)
    {
        DCHECK_NE(mime_type_lower_case, "");

        // Encrypted block support is never available without platform decoders.
        if (is_encrypted && !platform_info.has_platform_decoders)
            return false;

        // NOTE: We do not account for Media Source Extensions (MSE) within these
        // checks since it has its own isTypeSupported() which will handle platform
        // specific codec rejections.  See http://crbug.com/587303.

        switch (codec) {
        // ----------------------------------------------------------------------
        // The following codecs are never supported.
        // ----------------------------------------------------------------------
        case INVALID_CODEC:
        case AC3:
        case EAC3:
        case THEORA:
            return false;

            // ----------------------------------------------------------------------
            // The remaining codecs may be supported depending on platform abilities.
            // ----------------------------------------------------------------------

        case PCM:
        case MP3:
        case MPEG4_AAC:
        case FLAC:
        case VORBIS:
            // These codecs are always supported; via a platform decoder (when used
            // with MSE/EME), a software decoder (the unified pipeline), or with
            // MediaPlayer.
            DCHECK(!is_encrypted || platform_info.has_platform_decoders);
            return true;

        case MPEG2_AAC:
            // MPEG-2 variants of AAC are not supported on Android unless the unified
            // media pipeline can be used and the container is not HLS. These codecs
            // will be decoded in software. See https://crbug.com/544268 for details.
            if (base::EndsWith(mime_type_lower_case, "mpegurl",
                    base::CompareCase::SENSITIVE)) {
                return false;
            }
            return !is_encrypted && platform_info.is_unified_media_pipeline_enabled;

        case OPUS:
            // If clear, the unified pipeline can always decode Opus in software.
            if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled)
                return true;

            // Otherwise, platform support is required.
            if (!platform_info.supports_opus)
                return false;

            // MediaPlayer does not support Opus in ogg containers.
            if (base::EndsWith(mime_type_lower_case, "ogg",
                    base::CompareCase::SENSITIVE)) {
                return false;
            }

            DCHECK(!is_encrypted || platform_info.has_platform_decoders);
            return true;

        case H264:
            // When content is not encrypted we fall back to MediaPlayer, thus we
            // always support H264. For EME we need MediaCodec.
            return !is_encrypted || platform_info.has_platform_decoders;

        case HEVC:
#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
            if (platform_info.is_unified_media_pipeline_enabled && !platform_info.has_platform_decoders) {
                return false;
            }

#if defined(OS_ANDROID)
            // HEVC/H.265 is supported in Lollipop+ (API Level 21), according to
            // http://developer.android.com/reference/android/media/MediaFormat.html
            return base::android::BuildInfo::GetInstance()->sdk_int() >= 21;
#else
            return true;
#endif // defined(OS_ANDROID)
#else
            return false;
#endif // BUILDFLAG(ENABLE_HEVC_DEMUXING)

        case VP8:
            // If clear, the unified pipeline can always decode VP8 in software.
            if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled)
                return true;

            if (is_encrypted)
                return platform_info.has_platform_vp8_decoder;

            // MediaPlayer can always play VP8. Note: This is incorrect for MSE, but
            // MSE does not use this code. http://crbug.com/587303.
            return true;

        case VP9: {
            if (base::CommandLine::ForCurrentProcess()->HasSwitch(
                    switches::kReportVp9AsAnUnsupportedMimeType)) {
                return false;
            }

            // If clear, the unified pipeline can always decode VP9 in software.
            if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled)
                return true;

            if (!platform_info.has_platform_vp9_decoder)
                return false;

            // Encrypted content is demuxed so the container is irrelevant.
            if (is_encrypted)
                return true;

            // MediaPlayer only supports VP9 in WebM.
            return mime_type_lower_case == "video/webm";
        }
        }

        return false;
    }

    bool MimeUtil::StringToCodec(const std::string& mime_type_lower_case,
        const std::string& codec_id,
        Codec* codec,
        bool* is_ambiguous,
        VideoCodecProfile* out_profile,
        uint8_t* out_level,
        bool is_encrypted) const
    {
        DCHECK(out_profile);
        DCHECK(out_level);
        *out_profile = VIDEO_CODEC_PROFILE_UNKNOWN;
        *out_level = 0;

        StringToCodecMappings::const_iterator itr = string_to_codec_map_.find(codec_id);
        if (itr != string_to_codec_map_.end()) {
            *codec = itr->second.codec;
            *is_ambiguous = itr->second.is_ambiguous;
            return true;
        }

        // If |codec_id| is not in |string_to_codec_map_|, then we assume that it is
        // either VP9, H.264 or HEVC/H.265 codec ID because currently those are the
        // only ones that are not added to the |string_to_codec_map_| and require
        // parsing.
        if (ParseVp9CodecID(mime_type_lower_case, codec_id, out_profile, out_level)) {
            *codec = MimeUtil::VP9;
            switch (*out_profile) {
            case VP9PROFILE_PROFILE0:
                // Profile 0 should always be supported if VP9 is supported.
                *is_ambiguous = false;
                break;
            default:
                // We don't know if the underlying platform supports these profiles.
                // Need to add platform level querying to get supported profiles
                // (crbug/604566).
                *is_ambiguous = true;
                break;
            }
            return true;
        }

        if (ParseAVCCodecId(codec_id, out_profile, out_level)) {
            *codec = MimeUtil::H264;
            switch (*out_profile) {
// HIGH10PROFILE is supported through fallback to the ffmpeg decoder
// which is not available on Android, or if FFMPEG is not used.
#if !defined(MEDIA_DISABLE_FFMPEG) && !defined(OS_ANDROID)
            case H264PROFILE_HIGH10PROFILE:
                if (is_encrypted) {
                    // FFmpeg is not generally used for encrypted videos, so we do not
                    // know whether 10-bit is supported.
                    *is_ambiguous = true;
                    break;
                }
// Fall through.
#endif

            case H264PROFILE_BASELINE:
            case H264PROFILE_MAIN:
            case H264PROFILE_HIGH:
                *is_ambiguous = !IsValidH264Level(*out_level);
                break;
            default:
                *is_ambiguous = true;
            }
            return true;
        }

#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
        if (ParseHEVCCodecId(codec_id, out_profile, out_level)) {
            *codec = MimeUtil::HEVC;
            *is_ambiguous = false;
            return true;
        }
#endif

        DVLOG(4) << __func__ << ": Unrecognized codec id " << codec_id;
        return false;
    }

    bool MimeUtil::IsCodecSupported(Codec codec,
        const std::string& mime_type_lower_case,
        bool is_encrypted) const
    {
        DCHECK_NE(codec, INVALID_CODEC);

#if defined(OS_ANDROID)
        if (!IsCodecSupportedOnPlatform(codec, mime_type_lower_case, is_encrypted,
                platform_info_)) {
            return false;
        }
#endif

        return allow_proprietary_codecs_ || !IsCodecProprietary(codec);
    }

    bool MimeUtil::IsCodecProprietary(Codec codec) const
    {
        switch (codec) {
        case INVALID_CODEC:
        case AC3:
        case EAC3:
        case MP3:
        case MPEG2_AAC:
        case MPEG4_AAC:
        case H264:
        case HEVC:
            return true;

        case PCM:
        case VORBIS:
        case OPUS:
        case FLAC:
        case VP8:
        case VP9:
        case THEORA:
            return false;
        }

        return true;
    }

    bool MimeUtil::GetDefaultCodecLowerCase(const std::string& mime_type_lower_case,
        Codec* default_codec) const
    {
        if (mime_type_lower_case == "audio/mpeg" || mime_type_lower_case == "audio/mp3" || mime_type_lower_case == "audio/x-mp3") {
            *default_codec = MimeUtil::MP3;
            return true;
        }

        if (mime_type_lower_case == "audio/aac") {
            *default_codec = MimeUtil::MPEG4_AAC;
            return true;
        }

        if (mime_type_lower_case == "audio/flac") {
            *default_codec = MimeUtil::FLAC;
            return true;
        }

        return false;
    }

    bool MimeUtil::IsDefaultCodecSupportedLowerCase(
        const std::string& mime_type_lower_case,
        bool is_encrypted) const
    {
        Codec default_codec = Codec::INVALID_CODEC;
        if (!GetDefaultCodecLowerCase(mime_type_lower_case, &default_codec))
            return false;
        return IsCodecSupported(default_codec, mime_type_lower_case, is_encrypted);
    }

} // namespace internal
} // namespace media
